package com.ds.infrastructure.hyperspace.console.service.impl;

import com.ds.infrastructure.hyperspace.console.config.proxy.ProxyMetricsConfiguration;
import com.ds.infrastructure.hyperspace.console.entity.enums.TbDeployNodeEnum;
import com.ds.infrastructure.hyperspace.console.entity.enums.TbNodeMetricsEnum;
import com.ds.infrastructure.hyperspace.console.entity.enums.TbPublicEnum;
import com.ds.infrastructure.hyperspace.console.entity.page.TbPageWhere;
import com.ds.infrastructure.hyperspace.console.entity.table.*;
import com.ds.infrastructure.hyperspace.console.entity.where.TbMetricsTaskCustomWhere;
import com.ds.infrastructure.hyperspace.console.entity.where.TbNodeMetricsAggregationCustomWhere;
import com.ds.infrastructure.hyperspace.console.model.response.dashboard.EnvironmentMetricsVO;
import com.ds.infrastructure.hyperspace.console.model.response.dashboard.StageMetricsVO;
import com.ds.infrastructure.hyperspace.console.model.response.dashboard.SummaryMetricsVO;
import com.ds.infrastructure.hyperspace.console.model.response.diagram.SeriesChart;
import com.ds.infrastructure.hyperspace.console.service.inf.*;
import com.ds.infrastructure.hyperspace.console.utils.MetricsUtil;
import org.apache.commons.lang3.time.DateUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * @author: yaozhendong
 * @create: 2019-11-06 14:11
 **/
@Service
public class DashboardService implements IDashboardService {
    @Autowired
    IStageService stageService;
    @Autowired
    IDeployInstallTaskService iDeployInstallTaskService;
    @Autowired
    IDeployManageService iDeployManageService;
    @Resource
    ProxyMetricsConfiguration proxyMetricsConfiguration;
    @Autowired
    INodeMetricsService iNodeMetricsService;
    @Autowired
    IMetricsTaskService iMetricsTaskService;
    @Autowired
    IDeployNodeService iDeployNodeService;
    @Autowired
    INodeMetricsAggregationService iNodeMetricsAggregationService;
    @Autowired
    IEnvironmentService iEnvironmentService;
    @Autowired
    IStageService iStageService;
    @Autowired
    IDeployConfigService iDeployConfigService;
    @Autowired
    IContainerActiveConfigService iContainerActiveConfigService;

    @Override
    public SummaryMetricsVO getSummaryMetrics() {
        SummaryMetricsVO summaryMetricsVO = new SummaryMetricsVO();
        List<EnvironmentMetricsVO> environmentMetricsVOList = new ArrayList<>();
        //
        addEnvironmentMetrics(environmentMetricsVOList);
        //
        addSummaryBaseMetrics(summaryMetricsVO, environmentMetricsVOList);
        //
        addRequestCountsList(summaryMetricsVO);
        //
        summaryMetricsVO.setEnvironments(environmentMetricsVOList);
        //
        return summaryMetricsVO;
    }

    private void addRequestCountsList(SummaryMetricsVO summaryMetricsVO) {
        //最近16分钟的数据
        Date now = new Date();
        TbNodeMetricsAggregationCustomWhere customWhere = new TbNodeMetricsAggregationCustomWhere()
                .setExecuteTime4BeginTime(MetricsUtil.dateToTaskExecuteTime(DateUtils.addMinutes(now, -16)))
                .setExecuteTime4EndTime(MetricsUtil.dateToTaskExecuteTime(now));
        List<TbResultMap> requestCountsMap = iNodeMetricsAggregationService.sumRequestSecondsCount(customWhere);
        if (requestCountsMap == null || requestCountsMap.isEmpty()) {
            return;
        }
        List<SeriesChart> seriesChartList = new ArrayList<>();
        for (TbResultMap tbResultMap : requestCountsMap) {
            SeriesChart chart = new SeriesChart();
            chart.setTag("Gateway");
            chart.setYAxis(Long.parseLong(tbResultMap.getMapValue()));
            chart.setXAxis(Long.parseLong(tbResultMap.getMapKey()));
            seriesChartList.add(chart);
        }
        summaryMetricsVO.setRequestCountList(seriesChartList);
    }

    private void addSummaryBaseMetrics(SummaryMetricsVO summaryMetricsVO, List<EnvironmentMetricsVO> environmentMetricsVOList) {
        Integer environmentCount = 0;
        Integer instanceCount = 0;
        Integer proxyCount = 0;
        Long requestCount = 0L;
        for (EnvironmentMetricsVO environmentMetricsVO : environmentMetricsVOList) {
            environmentCount++;
            List<StageMetricsVO> stages = environmentMetricsVO.getStages();
            if (stages == null || stages.isEmpty()) {
                continue;
            }
            for (StageMetricsVO stage : stages) {
                instanceCount += Math.max(stage.getInstanceCount(), 0);
                proxyCount += Math.max(stage.getProxyCount(), 0);
                requestCount += Math.max(stage.getRequestCount(), 0);
            }
        }
        summaryMetricsVO.setEnvironmentCount(environmentCount);
        summaryMetricsVO.setInstanceCount(instanceCount);
        summaryMetricsVO.setProxyCount(proxyCount);
        summaryMetricsVO.setRequestTotal(requestCount);
    }

    private void addEnvironmentMetrics(List<EnvironmentMetricsVO> environmentMetricsVOList) {
        List<TbEnvironment> environmentList = iEnvironmentService.getAll();
        List<Long> environmentIds = environmentList.stream().map(TbEnvironment::getId).collect(Collectors.toList());
        List<TbStage> stageList = iStageService.getAllByEnvironmentIds(environmentIds);
        for (TbEnvironment tbEnvironment : environmentList) {
            toEnvironmentMetricsVO(tbEnvironment, stageList, environmentMetricsVOList);
        }
    }

    private void toEnvironmentMetricsVO(TbEnvironment tbEnvironment, List<TbStage> stageList, List<EnvironmentMetricsVO> environmentMetricsVOList) {
        EnvironmentMetricsVO environmentMetricsVO = new EnvironmentMetricsVO();
        environmentMetricsVO.setEnvironmentName(tbEnvironment.getEnvironmentName());
        environmentMetricsVO.setId(tbEnvironment.getId());
        environmentMetricsVO.setParentId(tbEnvironment.getParentId());
        List<StageMetricsVO> stageMetricsVOList = new ArrayList<>();
        List<TbStage> newStageList = stageList.stream().filter(item -> item.getEnvironmentId().equals(tbEnvironment.getId())).collect(Collectors.toList());
        for (TbStage tbStage : newStageList) {
            StageMetricsVO stageMetricsVO = new StageMetricsVO();
            stageMetricsVO.setId(tbStage.getId());
            stageMetricsVO.setStageName(tbStage.getStageName());
            //
            addDeployVersionMetrics(tbStage, stageMetricsVO);
            //
            addDeployInstanceCountMetrics(tbStage, stageMetricsVO);
            //
            addProxyCountMetrics(tbStage, stageMetricsVO);
            //
            addGatewayMetrics(tbStage, stageMetricsVO);
            //
            stageMetricsVOList.add(stageMetricsVO);
        }
        environmentMetricsVO.setStages(stageMetricsVOList);
        environmentMetricsVOList.add(environmentMetricsVO);
    }

    private void addProxyCountMetrics(TbStage tbStage, StageMetricsVO stageMetricsVO) {
        TbContainerActiveConfig tbContainerActiveConfig = iContainerActiveConfigService.selectLastOneByStageId(tbStage.getId());
        if (tbContainerActiveConfig == null) {
            return;
        }
        stageMetricsVO.setProxyCount(tbContainerActiveConfig.getProxyCount());
    }

    private void addGatewayMetrics(TbStage tbStage, StageMetricsVO stageMetricsVO) {
        TbMetricsTask tbMetricsTask = getLastCompleteMetricsTask(tbStage.getId());
        if (tbMetricsTask == null) {
            return;
        }
        List<TbNodeMetricsAggregation> tbNodeMetricsAggregationList = getNodeAggregationMetricsByMetricsTaskId(tbMetricsTask.getId());
        if (tbNodeMetricsAggregationList.isEmpty()) {
            return;
        }
        Long requestCount = statisticsGatewayRequestCount(tbNodeMetricsAggregationList);
        Long gatewayErrorCount = statisticsRequestErrorCountMetrics(tbNodeMetricsAggregationList, TbNodeMetricsEnum.ENDPOINT_TYPE_TAG.GATEWAY);
        Long targetErrorCount = statisticsRequestErrorCountMetrics(tbNodeMetricsAggregationList, TbNodeMetricsEnum.ENDPOINT_TYPE_TAG.TARGET);
        stageMetricsVO.setRequestCount(requestCount);
        stageMetricsVO.setGatewayErrorCount(gatewayErrorCount);
        stageMetricsVO.setTargetErrorCount(targetErrorCount);
    }

    private Long statisticsRequestErrorCountMetrics(List<TbNodeMetricsAggregation> tbNodeMetricsAggregationList, TbNodeMetricsEnum.ENDPOINT_TYPE_TAG endpointTypeTag) {
        Long defaultValue = 0L;
        if (tbNodeMetricsAggregationList == null || tbNodeMetricsAggregationList.isEmpty()) {
            return defaultValue;
        }
        Optional<Long> optional = tbNodeMetricsAggregationList.stream()
                .filter(item -> TbNodeMetricsEnum.METRICS_KEY.HYPERSPACE_ERRORS_TOTAL.key.equals(item.getMetricsKey()) &&
                        endpointTypeTag.key.equals(item.getEndpointType()))
                .map(TbNodeMetricsAggregation::getMetricsValue).reduce(Long::sum);
        if (optional.isPresent()) {
            return optional.get().longValue();
        }
        return defaultValue;
    }

    private Long statisticsGatewayRequestCount(List<TbNodeMetricsAggregation> tbNodeMetricsAggregationList) {
        Long defaultValue = 0L;
        if (tbNodeMetricsAggregationList == null || tbNodeMetricsAggregationList.isEmpty()) {
            return defaultValue;
        }
        Optional<TbNodeMetricsAggregation> tbNodeMetricsAggregationOptional = tbNodeMetricsAggregationList.stream()
                .filter(item -> TbNodeMetricsEnum.METRICS_KEY.HYPERSPACE_REQUEST_SECONDS_COUNT.key.equals(item.getMetricsKey()) &&
                        TbNodeMetricsEnum.ENDPOINT_TYPE_TAG.GATEWAY.key.equals(item.getEndpointType())).findFirst();
        if (tbNodeMetricsAggregationOptional.isPresent()) {
            return tbNodeMetricsAggregationOptional.get().getMetricsValue();
        }
        return defaultValue;
    }

    private void addDeployVersionMetrics(TbStage tbStage, StageMetricsVO stageMetricsVO) {
        //部署版本号
        TbDeployConfig tbDeployConfig = iDeployConfigService.selectLastOneForInstalled(tbStage.getId(), tbStage.getEnvironmentId());
        if (tbDeployConfig == null) {
            return;
        }
        stageMetricsVO.setDeploymentVersion(tbDeployConfig.getConfigVersion());
    }

    private void addDeployInstanceCountMetrics(TbStage tbStage, StageMetricsVO stageMetricsVO) {
        //部署实例数量
        TbDeployInstallTask tbDeployInstallTask = iDeployInstallTaskService.selectLastOneForExecuteSuccess(tbStage.getId(), tbStage.getEnvironmentId());
        if (tbDeployInstallTask == null) {
            stageMetricsVO.setInstanceCount(0);
            return;
        }
        TbDeployNode where = new TbDeployNode();
        where.setDeployTaskId(tbDeployInstallTask.getId());
        where.setActiveState(TbDeployNodeEnum.ACTIVE_STATE.SUCCESS.code);
        where.setGmtIsDeleted(TbPublicEnum.GMD_IS_DEL.NO.code);
        Integer instanceCount = iDeployNodeService.getTotal(where, null);
        stageMetricsVO.setInstanceCount(instanceCount);
    }

    public TbMetricsTask getLastCompleteMetricsTask(Long stageId) {
        TbMetricsTask where = new TbMetricsTask();
        where.setStageId(stageId);
        where.setCompleteState(TbPublicEnum.TASK_COMPLETE_STATE.COMPLETE.value);
        TbMetricsTaskCustomWhere custom = new TbMetricsTaskCustomWhere().setOrderByType(TbPublicEnum.ORDER_BY_TYPE.ID_DESC.value);
        return iMetricsTaskService.selectOne(where, custom);
    }


    public List<TbNodeMetricsAggregation> getNodeAggregationMetricsByMetricsTaskId(Long metricsTaskId) {
        TbNodeMetricsAggregation where = new TbNodeMetricsAggregation();
        where.setMetricsTaskId(metricsTaskId);
        return iNodeMetricsAggregationService.getList(where, null, TbPageWhere.newPage4All());
    }
}
